package org.dreamwork.jasmine2.restful.client.gui.util;

import org.dreamwork.jasmine2.restful.*;
import org.dreamwork.util.StringUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2014/12/22
 * Time: 19:27
 */
public class ClassGenerator {
    private String packageName, prefix, suffix;
    private File base;
    private APIDefinition api;

    public ClassGenerator (String packageName, String prefix, String suffix, APIDefinition api, File base) {
        this.packageName = packageName;
        this.prefix = prefix;
        this.suffix = suffix;
        this.base = base;
        this.api = api;
    }

    public void generate () throws IOException {
        String path = packageName.replace ('.', '/');
        File dir = new File (base, path);
        if (!dir.exists () && !dir.mkdirs ()) {
            throw new IOException ("Can't create dir: " + dir.getCanonicalPath ());
        }

        String pattern = api.getPattern ();
        pattern = upper (pattern);
        if (!StringUtil.isEmpty (prefix)) {
            pattern = upper (prefix.trim ()) + pattern;
        }
        if (!StringUtil.isEmpty (suffix)) {
            pattern = pattern + upper (suffix.trim ());
        }
        File file = new File (dir, pattern + ".java");
        FileOutputStream fos = new FileOutputStream (file);
        try {
            PrintWriter out = new PrintWriter (fos, true);
            out.println ("package " + packageName + ";");
            out.println ();
            out.println ("/**");
            out.println (" * Generated by jasmine2 restful-api client.");
            out.println (" * " + new Date ());
            out.println (" */");
            out.println ("public class " + toJavaClassName (pattern) + " {");
            for (MethodDefinition md : api.getMethods ().values ()) {
                generateMethodClass (md, out);
            }
            for (MethodDefinition md : api.getMethods ().values ()) {
                generateMethod (md, out);
            }
            out.print ("}");
            out.flush ();
        } finally {
            fos.flush ();
            fos.close ();
        }
    }

    private String upper (String text) {
        return Character.toUpperCase (text.charAt (0)) + text.substring (1);
    }

    private void generateMethodClass (MethodDefinition md, PrintWriter out) {
        String name = md.getPattern ();
        String className = toJavaClassName (name);
        out.println ("\tprivate static class " + className + " extends RestfulAPIClient {");
        out.println ("\t\tprivate " + className + " () {");
        out.println ("\t\t\tencrypted = " + (api.isForceEncrypt () || md.isForceEncrypt ()) + ";");
        out.println ("\t\t\tmethod = HttpMethod." + md.getType () + ";");
        out.println ("\t\t\tpattern = \"" + api.getPattern () + '/' + md.getPattern () + "\";");

        Collection<ContextParameterDefinition> c = new HashSet<ContextParameterDefinition> ();
        c.addAll (api.getContextParameters ());
        c.addAll (md.getContextParameters ());
        for (ContextParameterDefinition cpd : c) {
            out.printf (
                    "\t\t\tcontextParameters.add (new Parameter (\"%s\", %s));%n",
                    cpd.getName (), cpd.isRequired ()
            );
        }

        out.println ("\t\t}");
        out.println ("\t}");
        out.println ();
        out.flush ();
    }

    private void generateMethod (MethodDefinition md, PrintWriter out) {
        out.println ();
        out.print ("\tpublic static String " + toJavaMethodName (md.getPattern ()) + " (");
        Collection<ContextParameterDefinition> c = new HashSet<ContextParameterDefinition> ();
        c.addAll (api.getRequiredContextParameter ());
        c.addAll (md.getRequiredContextParameter ());
        int index = 0;
        for (ContextParameterDefinition cpd : c) {
            if (index > 0) out.print (", ");
            out.print (cpd.getTypeName () + ' ' + cpd.getName ());
            index ++;
        }
        List<APIParameterDefinition> list = md.getParameters ();
        for (APIParameterDefinition pd : list) {
            if (index > 0) out.print (", ");
            out.print (pd.getClassName () + ' ' + pd.getName ());
            index ++;
        }
        out.println (") throws RestfulClientException, java.io.IOException {");
        String className = toJavaClassName (md.getPattern ());
        out.printf ("\t\t%s client = new %s ();%n", className, className);
        for (ContextParameterDefinition cpd : c) {
            if ("java.lang.String".equals (cpd.getTypeName ()))
                out.printf ("\t\tclient.addParameter (\"%s\", %s);%n", cpd.getName (), cpd.getName ());
            else
                out.printf ("\t\tclient.addParameter (\"%s\", String.valueOf (%s));%n", cpd.getName (), cpd.getName ());
        }
        if (md.getType () == MethodType.POST) {
            if (list.size () > 0) {
                out.println ("\t\tcom.google.gson.Gson g = org.dreamwork.gson.GsonHelper.getGson (true, true);");
                if (list.size () == 1) {
                    out.printf ("\t\tclient.body = g.toJson (%s);%n", list.get (0).getName ());
                } else if (list.size () > 1) {
                    out.println ("\t\tjava.util.Map<String, Object> map = new java.util.HashMap<String, Object> ();");
                    for (APIParameterDefinition pd : list) {
                        out.printf ("\t\tmap.put (\"%s\", %s);%n", pd.getName (), pd.getName ());
                    }
                    out.println ("\t\tclient.body = g.toJson (map);");
                }
            }
        } else {
            for (APIParameterDefinition pd : list) {
                if (pd.getClassName ().equals ("java.lang.String")) {
                    out.printf ("\t\tclient.addParameter (\"%s\", %s);%n", pd.getName (), pd.getName ());
                } else {
                    out.printf ("\t\tclient.addParameter (\"%s\", String.valueOf (%s));%n", pd.getName (), pd.getName ());
                }
            }
        }
        out.println ("\t\treturn client.call ();");
        out.println ("\t}");
    }

    private String toJavaClassName (String name) {
        String[] parts = name.split ("[-/]");
        StringBuilder builder = new StringBuilder ();
        for (String p : parts) {
            builder.append (Character.toUpperCase (p.charAt (0))).append (p.substring (1));
        }
        return builder.toString ();
    }

    private String toJavaMethodName (String name) {
        String[] parts = name.split ("[-/]");
        StringBuilder builder = new StringBuilder ();
        for (int i = 0; i < parts.length; i ++) {
            String p = parts [i];
            if (i == 0)
                builder.append (Character.toLowerCase (p.charAt (0)));
            else
                builder.append (Character.toUpperCase (p.charAt (0)));
            builder.append (p.substring (1));
        }
        return builder.toString ();
    }
}